package com.swak.define;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Map;

import com.swak.annotation.Table;
import com.swak.utils.Maps;

/**
 * 表定义
 * 
 * @author lifeng
 * @date 2021年1月22日 上午9:35:19
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public class TableDefines {

    private static TableDefines CONTEXT = TableDefinesHodler.CONTEXT;

    private static class TableDefinesHodler {
        static TableDefines CONTEXT = new TableDefines();
    }

    private Map<Class<?>, TableDefine> defines = Maps.newConcurrentMap();
    private Map<String, TableDefine> namespaces = Maps.newConcurrentMap();

    protected TableDefine computeIfAbsent(Class<?> mapperClass) {
        Class<?> modelClass = extractModelClass(mapperClass);
        if (modelClass == null || modelClass.getAnnotation(Table.class) == null) {
            throw new RuntimeException("Please define @Table in class " + modelClass);
        }
        if (!defines.containsKey(modelClass)) {
            return defines.computeIfAbsent(modelClass, (key) -> {
                TableDefine define = new TableDefine(mapperClass, modelClass);
                namespaces.put(define.getNameSpace(), define);
                return define;
            });
        }
        return defines.get(modelClass);
    }

    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     */
    public static Class<?> extractModelClass(Class<?> mapperClass) {
        if (mapperClass.isInterface()) {
            return extractModelClass4Interface(mapperClass);
        }
        return extractModelClass4Class(mapperClass);
    }

    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     */
    public static Class<?> extractModelClass4Class(Class<?> mapperClass) {
        Type type = mapperClass.getGenericSuperclass();
        ParameterizedType target = null;
        if (type instanceof ParameterizedType) {
            Type[] typeArray = ((ParameterizedType)type).getActualTypeArguments();
            if (typeArray != null && typeArray.length > 0) {
                for (Type t : typeArray) {
                    if (t instanceof TypeVariable || t instanceof WildcardType) {
                        break;
                    } else {
                        target = (ParameterizedType)type;
                        break;
                    }
                }
            }
        }
        return target == null ? null : (Class<?>)target.getActualTypeArguments()[0];
    }

    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     */
    public static Class<?> extractModelClass4Interface(Class<?> mapperClass) {
        Type[] types = mapperClass.getGenericInterfaces();
        ParameterizedType target = null;
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] typeArray = ((ParameterizedType)type).getActualTypeArguments();
                if (typeArray != null && typeArray.length > 0) {
                    for (Type t : typeArray) {
                        if (t instanceof TypeVariable || t instanceof WildcardType) {
                            break;
                        } else {
                            target = (ParameterizedType)type;
                            break;
                        }
                    }
                }
                break;
            }
        }
        return target == null ? null : (Class<?>)target.getActualTypeArguments()[0];
    }

    /**
     * 获取表定义
     * 
     * @param <T>
     * @param entity
     * @return
     */
    public static <T> TableDefine<T> lookup(Class<T> modelClass) {
        return CONTEXT.defines.get(modelClass);
    }

    /**
     * 获取表定义: 通过命名空间查找
     * 
     * @param <T>
     * @param entity
     * @return
     */
    public static <T> TableDefine<T> lookup(String namespace) {
        return CONTEXT.namespaces.get(namespace);
    }

    /**
     * 定义表： 转换成命名空间，定义和使用的地方是分开的
     * 
     * @param <T>
     * @param entity
     * @return
     */
    public static <T> TableDefine<T> newDefineUseMapper(Class<?> mapperClass) {
        return CONTEXT.computeIfAbsent(mapperClass);
    }
}